package com.example.demo.juc.cas;

/**
 * @author MarioZzz
 * @create 2024-05-08 16:53
 */

import java.lang.reflect.Field;
import java.util.Random;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;

public class DragonBoatRace {
    private static final int TEAM_NUM = 4; // 参赛队伍数
    private static final int BOAT_NUM = 1; // 龙舟数量
    private static final Random random = new Random();
    private static final AtomicInteger time = new AtomicInteger(0);
    private static final CountDownLatch startLatch = new CountDownLatch(TEAM_NUM);
    private static final CyclicBarrier finishBarrier = new CyclicBarrier(TEAM_NUM + 1);
    private static final LockFreeQueue<Integer> queue = new LockFreeQueue<>();

    public static void main(String[] args) throws InterruptedException, BrokenBarrierException {
        ExecutorService executorService = Executors.newFixedThreadPool(TEAM_NUM);
        for (int i = 0; i < TEAM_NUM; i++) {
            executorService.submit(new Team(i + 1));
        }
        startLatch.await(); // 等待所有队伍准备就绪
        System.out.println("比赛开始！");
        for (int i = 0; i < BOAT_NUM; i++) {
            new Thread(new Boat(i + 1)).start();
        }
        System.out.println("龙舟已经准备好！");
        finishBarrier.await(); // 等待所有队伍完成比赛
        System.out.println("所有队伍完成比赛，开始庆祝！");
        executorService.shutdown(); // 关闭线程池
    }

    static class Team implements Runnable {
        private final int teamId;

        public Team(int teamId) {
            this.teamId = teamId;
        }

        @Override
        public void run() {
            try {
                Thread.sleep(1000 * teamId); // 模拟队伍准备时间
                System.out.println("队伍" + teamId + "已准备就绪！");
                startLatch.countDown(); // 准备就绪，计数器减一
                queue.add(1); // 龙舟前进一步
                while (time.get() < 2000) { // 等待龙舟到达终点
                    Thread.yield();
                }
                System.out.println("队伍" + teamId + "已完成比赛，用时" + time.get() + "ms！");
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                try {
                    finishBarrier.await(); // 等待其他队伍完成比赛
                    System.out.println("队伍" + teamId + "正在庆祝！");
                } catch (InterruptedException | BrokenBarrierException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    static class Boat implements Runnable {
        private final int boatId;

        public Boat(int boatId) {
            this.boatId = boatId;
        }

        @Override
        public void run() {
            while (!queue.isEmpty()) { // 龙舟前进，直到到达终点
                if (queue.poll() != null) {
                    time.addAndGet(random.nextInt(10) + 1); // 龙舟前进一步，用时1~10ms
                }
            }
            try {
                finishBarrier.await(); // 等待所有队伍完成比赛
            } catch (InterruptedException | BrokenBarrierException e) {
                e.printStackTrace();
            }
        }
    }

    static class LockFreeQueue<E> {
        private static class Node<E> {
            final E item;
            volatile Node<E> next;

            Node(E item) {
                this.item = item;
            }
        }

        private volatile Node<E> head;
        private volatile Node<E> tail;

        public boolean isEmpty() {
            return head == null;
        }

        public void add(E item) {
            Node<E> node = new Node<>(item);
            while (true) {
                Node<E> last = tail;
                Node<E> next = last == null ? head : last.next;
                if (last == tail) {
                    if (next == null) {
                        if (compareAndSetNext(last, null, node)) {
                            compareAndSetTail(last, node);
                            return;
                        }
                    } else {
                        compareAndSetTail(last, next);
                    }
                }
            }
        }

        public E poll() {
            while (true) {
                Node<E> first = head;
                Node<E> last = tail;
                Node<E> next = first == null ? null : first.next;
                if (first == head) {
                    if (first == last) {
                        if (next == null) {
                            return null;
                        }
                        compareAndSetTail(last, next);
                    } else {
                        E item = next.item;
                        if (compareAndSetHead(first, next)) {
                            return item;
                        }
                    }
                }
            }
        }

        private boolean compareAndSetHead(Node<E> expect, Node<E> update) {
            return unsafe.compareAndSwapObject(this, headOffset, expect, update);
        }

        private boolean compareAndSetTail(Node<E> expect, Node<E> update) {
            return unsafe.compareAndSwapObject(this, tailOffset, expect, update);
        }

        private boolean compareAndSetNext(Node<E> node, Node<E> expect, Node<E> update) {
            return unsafe.compareAndSwapObject(node, nextOffset, expect, update);
        }

        private static final sun.misc.Unsafe unsafe;
        private static final long headOffset;
        private static final long tailOffset;
        private static final long nextOffset;

        static {
            try {
                unsafe = getUnsafe();
                Class<?> k = LockFreeQueue.class;
                headOffset = unsafe.objectFieldOffset(k.getDeclaredField("head"));
                tailOffset = unsafe.objectFieldOffset(k.getDeclaredField("tail"));
                nextOffset = unsafe.objectFieldOffset(Node.class.getDeclaredField("next"));
            } catch (Exception e) {
                throw new Error(e);
            }
        }

        private static sun.misc.Unsafe getUnsafe() throws Exception {
            Field field = sun.misc.Unsafe.class.getDeclaredField("theUnsafe");
            field.setAccessible(true);
            return (sun.misc.Unsafe) field.get(null);
        }
    }
}


